#pragma once

#include <iostream>
#include <cassert>
using namespace std;

template<class T>
class vector
{
	typedef T* iterator;
	typedef const T* const_iterator;
public:
	iterator begin()
	{
		return _start;
	}
	iterator end()
	{
		return _finish;
	}
	const_iterator begin() const
	{
		return _start;
	}
	const_iterator end() const
	{
		return _finish;
	}

	vector() = default;

	vector(initializer_list<T> l)
	{
		reserve(l.size());
		for (const auto& e : l)
		{
			push_back(e);
		}
	}

	template<class InputIterator>
	vector(InputIterator first, InputIterator last)
	{
		while (first != last)
		{
			push_back(*first);
			first++;
		}
	}

	vector(size_t n, const T& val = T())
	{
		reserve(n);
		for (size_t i = 0; i < n; i++)
		{
			push_back(val);
		}
	}

	vector(const vector<T>& v)
	{
		reserve(v.capacity());
		for (const auto& e : v)
		{
			push_back(e);
		}
	}

	vector<T>& operator=(vector<T> v)
	{
		swap(v);
		return *this;
	}

	~vector()
	{
		if (_start)
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}
	}

	T& operator[](size_t i)
	{
		assert(i < size());
		return _start[i];
	}

	const T& operator[](size_t i) const
	{
		assert(i < size());
		return _start[i];
	}

	size_t size() const
	{
		return _finish - _start;
	}

	size_t capacity() const
	{
		return _end_of_storage - _start;
	}

	bool empty()
	{
		return _start == _finish;
	}

	void swap(vector<T>& v)
	{
		std::swap(_start, v._start);
		std::swap(_finish, v._finish);
		std::swap(_end_of_storage, v._end_of_storage);
	}

	void resize(size_t n, const T& val = T())
	{
		if (n <= size())
		{
			_finish = _start + n;
		}
		else
		{
			reserve(n);
			while (_finish < _start + n)
			{
				*_finish = val;
				_finish++;
			}
		}
	}

	void reserve(size_t n)
	{
		if (n > capacity())
		{
			size_t old_size = size();
			T* tmp = new T[n];
			if (_start)
			{
				for (size_t i = 0; i < old_size; i++)
				{
					tmp[i] = _start[i];
				}
				delete[] _start;
			}
			_start = tmp;
			_finish = tmp + old_size;
			_end_of_storage = tmp + n;
		}
	}

	iterator insert(iterator pos, const T& val)
	{
		assert(pos >= _start && pos <= _finish);
		if (_finish == _end_of_storage)
		{
			size_t len = pos - _start;
			reserve(capacity() == 0 ? 4 : 2 * capacity());
			pos = _start + len;
		}
		iterator i = _finish - 1;
		while (i >= pos)
		{
			*(i + 1) = *i;
			i--;
		}
		*pos = val;
		_finish++;
		return pos;
	}

	void push_back(const T& val)
	{
		insert(_finish, val);
	}

	iterator erase(iterator pos)
	{
		assert(pos >= _start && pos < _finish);
		iterator i = pos + 1;
		while (i < _finish)
		{
			*(i - 1) = *i;
		}
		_finish--;
		return pos;
	}
	void pop_back()
	{
		assert(!empty());
		_finish--;
	}
private:
	iterator _start = nullptr;
	iterator _finish = nullptr;
	iterator _end_of_storage = nullptr;
};